/* -+- file  ./kf2/Core/SqliteArchive.cs -+- */
/*
 * kf2 jabber client
 * ~~~~~~~~~~~~~~~~~
 *
 * Copyright (C) 2006-2007 Przemysław Sitek
 * 				<przemyslaw.sitek at wp.pl> 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Xml;
using Mono.Data.SqliteClient;
using Habazie.kf2.Core;
using Habazie.kf2.Net;

namespace Habazie.kf2.Core
{
	
	public class SqliteArchive : IArchivingService
	{
		private IDbConnection m_Sql;
		
		public SqliteArchive()
		{
			string connectionString = "URI=file:"+Util.UserFile("Archive.db")+",version=3";
			
			
			m_Sql = new SqliteConnection (connectionString);
			m_Sql.Open ();
			
			/* Check, if database is created */
			IDbCommand cmd = m_Sql.CreateCommand ();
			cmd.CommandText = "SELECT COUNT(*) FROM SQLITE_MASTER";
			Int64 tables = (Int64) cmd.ExecuteScalar ();
//			System.Console.WriteLine ("Query returned {0}",tables.GetType().FullName);
			if (tables == 0) {
				System.Console.WriteLine ("Creating schemas...");
				CreateSchemas ();
			}
		}

		
		public void Archive (Account acc, Message msg, string thread_id, bool incoming)
		{
			if (msg.Body == null)
				return;				// should we?
			
			Util.Debug ("core", "Archiving msg \"{0}\"", msg.Body);

			try {
			//tring remote_jid = incoming ? msg.From.Bare : msg.To.Bare;
				string from = msg.GetAttribute ("from");
				string to = msg.GetAttribute ("to");
			
				// message date									
				System.DateTime t = System.DateTime.Now;
			
				IDbCommand cmd = m_Sql.CreateCommand ();
				cmd.CommandText = "INSERT INTO Messages("
									+" Day, Month, Year, Account, ConvWith, Incoming, FromJid, ToJid, DateSent, DateRecv, MsgType, Subject, Body"
									+") VALUES("
									+"@Day,@Month,@Year,@Account,@ConvWith,@Incoming,@FromJid,@ToJid,@DateSent,@DateRecv,@MsgType,@Subject,@Body"
									+")";
				
				AddParameter (cmd, "@Day", t.Day);
				AddParameter (cmd, "@Month", t.Month);
				AddParameter (cmd, "@Year", t.Year);
				AddParameter (cmd, "@Account", acc.User+"@"+acc.Server+"/"+acc.Resource);
				AddParameter (cmd, "@ConvWith", thread_id);
				AddParameter (cmd, "@Incoming", incoming);
				AddParameter (cmd, "@FromJid", from);
				AddParameter (cmd, "@ToJid", to);
				AddParameter (cmd, "@DateSent", System.DateTime.Now); // FIXME
				AddParameter (cmd, "@DateRecv", System.DateTime.Now); // FIXME
				AddParameter (cmd, "@MsgType", msg.Type.ToString ());
				AddParameter (cmd, "@Subject", msg.Subject);
				AddParameter (cmd, "@Body", msg.Body);
				cmd.ExecuteNonQuery ();
		//	cmd.Parameters.Add
			} catch (Exception e) {
				Util.ReportException (e);
			}
		}
		
		
		private void CreateSchemas ()
		{
			const string QUERY = "CREATE TABLE Messages ("
									+"ID INTEGER PRIMARY KEY AUTOINCREMENT,"	// Auto generated key
									+"Day INTEGER,"								// Day when message was received
									+"Month INTEGER, "							// Month number
									+"Year INTEGER, "							// Year
									+"Account VARCHAR(64),"						// Account
									+"ConvWith VARCHAR(64),"					// Conversation identifier
									+"Incoming BOOL,"							// Whether message is incomings
									+"FromJid VARCHAR(64),"						// Sender of the message
									+"ToJid VARCHAR(64),"						// Receiver of the message
									+"DateSent DATETIME,"						// Date sent
									+"DateRecv DATETIME,"						// Date received
									+"MsgType VARCHAR(16),"						// Message type (chat message etc)
									+"Subject VARCHAR(256),"					// Message subject
									+"Body TEXT);";								// Message body
			IDbCommand cmd = m_Sql.CreateCommand ();
			cmd.CommandText = QUERY;
			cmd.ExecuteNonQuery ();
		}
		
		
		///<summary>
		/// Add a parameter to SQL query
		///</summary>
		private void AddParameter (IDbCommand cmd, string name, string val)
		{
			IDbDataParameter p1 = cmd.CreateParameter ();
			p1.ParameterName = name;
			p1.DbType = System.Data.DbType.String;
			p1.Value = val;
			cmd.Parameters.Add (p1);
		}
		
		
		///<summary>
		/// Add a parameter to SQL query
		///</summary>
		private void AddParameter (IDbCommand cmd, string name, int val)
		{
			IDbDataParameter p1 = cmd.CreateParameter ();
			p1.ParameterName = name;
			p1.DbType = System.Data.DbType.Int32;
			p1.Value = val;
			cmd.Parameters.Add (p1);
		}
		
		
		///<summary>
		/// Add a parameter to SQL query
		///</summary>
		private void AddParameter (IDbCommand cmd, string name, bool val)
		{
			IDbDataParameter p1 = cmd.CreateParameter ();
			p1.ParameterName = name;
			p1.DbType = System.Data.DbType.Boolean;
			p1.Value = val;
			cmd.Parameters.Add (p1);
		}
		
		///<summary>
		/// Add a parameter to SQL query
		///</summary>
		private void AddParameter (IDbCommand cmd, string name, System.DateTime val)
		{
			IDbDataParameter p1 = cmd.CreateParameter ();
			p1.ParameterName = name;
			p1.DbType = System.Data.DbType.DateTime;
			p1.Value = val;
			cmd.Parameters.Add (p1);
		}


		///	<summary>
		/// 	Execute a query on archive database
		/// </summary>
		public IMessageCollection ExecuteQuery (string query)
		{
			IDbCommand cmd = m_Sql.CreateCommand ();
			cmd.CommandText = query;
			return ExecuteQuery (cmd);
		}
		
		
		///	<summary>
		/// 	Execute a query on archive database
		/// </summary>
		public IMessageCollection ExecuteQuery (IDbCommand cmd)
		{
			IDataReader reader = cmd.ExecuteReader ();
			return new MessageCollection (reader);
		}
	}
	
	
	/// <summary>
	/// 	Represents a collection of messages
	/// </summary>
	public class MessageCollection : IMessageCollection, IEnumerable<Message>
	{
		private IDataReader m_reader;
		

		internal MessageCollection (IDataReader reader)
		{
			m_reader = reader;
		}

	
		/// <summary>
		/// 	Allow iterating through messages
		/// </summary>
		public IEnumerator<Message> GetEnumerator()
		{
			XmlDocument m_doc = new XmlDocument ();
			while (m_reader.Read ()) {
				yield return ReadMessage (m_reader, m_doc);
			}
		}
		
		
		/// <summary>
		/// 	Create a Message from database row
		/// </summary>
		protected virtual Message ReadMessage (IDataReader reader, XmlDocument doc)
		{
			Message msg	= new Message (doc);
			string from = (string) m_reader["FromJid"];
			string to	= (string) m_reader["ToJid"];
			if (from == null || from == "")
				if (m_reader.GetBoolean (6) == false) // Incoming
					from = (string) m_reader["Account"];
			
			msg.From	= new jabber.JID (from);
			msg.To		= new jabber.JID (to);
			msg.Subject = (string) m_reader["Subject"];
			msg.Body	= (string) m_reader["Body"];
			return msg;
		}
		
		/* Satisfy non-generic IEnumerable */
		IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } // aargh!
		
//		public IEnumerator GetEnumerator ()
//		{
//			return (IEnumerator) GetEnumerator ();
//		}
//		internal class Enumerator : IEnumerator
//		{
//			private XmlDocument m_doc;
//			private IDataReader m_reader = null;
//			
//			public Enumerator (IDataReader r)
//			{
//				m_doc = new XmlDocument ();
//				m_reader = r;
//			}
//			
//			
//			public object Current {
//				get {
//					Message msg	= new Message (m_doc);
//					msg.From	= new jabber.JID ((string) m_reader["FromJid"]);
//					msg.To		= new jabber.JID ((string) m_reader["ToJid"]);
//					msg.Subject = (string) m_reader["Subject"];
//					msg.Body	= (string) m_reader["Body"];
//					return (object) msg;
//				}
//			}
//			
//			public bool MoveNext()
//			{
//				return m_reader.Read ();
//			}
//			
//			public void Reset()
//			{
////				m_reader.
//			}
//		}
//		
//		private IDataReader m_reader;
//		
//		public IEnumerator GetEnumerator ()
//		{
//			return new Enumerator (m_reader);
//		}
//		
//	
//		
//		
//		internal MessageCollection (IDataReader reader)
//		{
//			m_reader = reader;
//		}
	}
}
